﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Core.Collections;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Scene;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.IntersectionEngines;

namespace RayDen.RayEngine.Data
{
    public class SceneDataInfo
    {
        public SceneGeometryInfo GeoData;
        public TriangleDataInfo[] Triangles;
        public List<TriangleMeshInfo> Meshes;

        private readonly Dictionary<int, TriangleMeshInfo> meshTriangleMap;
        private readonly TriangleMeshInfo[] meshesMap;

        public SceneDataInfo(SceneGeometryInfo g)
        {
            this.meshTriangleMap = new Dictionary<int, TriangleMeshInfo>();
            this.Meshes = new List<TriangleMeshInfo>();
            this.BuildSceneData(g);
            meshesMap = meshTriangleMap.Values.ToArray();
        }
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public TriangleMeshInfo GetMeshByTriangleIndex(int currentTriangleIndex)
        {
#if VERBOSE
            try
            {
#endif
                TriangleMeshInfo m;
                return meshesMap[currentTriangleIndex];
#if VERBOSE
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + currentTriangleIndex);
            }
            return null;
#endif
            //if (!meshTriangleMap.TryGetValue(currentTriangleIndex, out m))
            //{
            //    throw new ArgumentException("Mesh for triangle "+currentTriangleIndex+ " not found");
            //}
            //return m;

            //for (int index = 0; index < Meshes.Count; index++)
            //{
            //    if (Meshes[index].TriangleInRange(currentTriangleIndex))
            //        return Meshes[index];
            //}
            //return null;
        }

        private void BuildSceneData(SceneGeometryInfo geometry)
        {
            this.GeoData = geometry;
            int verticesCount = 0;
            var triangles = new List<TriangleDataInfo>();
            foreach (var geometryInfo in GeoData.Geometry)
            {
                var mesh = new TriangleMeshInfo(this)
                    {
                        MeshProfile = this.CreateGeometryProfile(geometryInfo),
                        MaterialName = geometryInfo.MaterialName,
                        MeshName = geometryInfo.Name,
                    };
                this.Meshes.Add(mesh);

                var model = geometryInfo;
                var delta = 0;
                var index = 0;
                bool hasNormals = false;
                int[] ni = null;

                bool hasTexCoords = false;
                int[] ti = null;
                var startTriangle = Math.Max(0, triangles.Count);

                if (model.IndexData.Any())
                {
                    hasNormals = model.NormalIndexData != null && model.NormalIndexData.Any();
                    hasTexCoords = model.TextureIndexData != null && model.TextureIndexData.Any();
                    if (hasTexCoords)
                    {
                        if (!GeoData.TexCoords.Any())
                            hasTexCoords = false;
                        ti = model.TextureIndexData.ToArray();
                    }
                    if (hasNormals)
                    {
                        ni = model.NormalIndexData.ToArray();
                    }
                    mesh.HasNormals = hasNormals;
                    mesh.HasTexCoords = hasTexCoords;

                    for (int i = 0; i < model.IndexData.Count; i += 3)
                    {
                        index++;

                        var newTriangle = new TriangleDataInfo()
                            {
                                v0 = new MeshVertexInfo()
                                    {
                                        NormalIndex = hasNormals ? ni[i] - 1 : -1,
                                        VertexIndex = model.IndexData[i] - delta,
                                        TexCoordIndex = hasTexCoords ? ti[i] - 1 : -1
                                    },
                                v1 = new MeshVertexInfo()
                                    {
                                        NormalIndex = hasNormals ? ni[i + 1] - 1 : -1,
                                        VertexIndex = model.IndexData[i + 1] - delta,
                                        TexCoordIndex = hasTexCoords ? ti[i + 1] - 1 : -1
                                    },
                                v2 = new MeshVertexInfo()
                                    {
                                        NormalIndex = hasNormals ? ni[i + 2] - 1 : -1,
                                        VertexIndex = model.IndexData[i + 2] - delta,
                                        TexCoordIndex = hasTexCoords ? ti[i + 2] - 1 : -1
                                    },

                            };
                        //model.IndexData[i] - delta, model.IndexData[i + 1] - delta, model.IndexData[i + 2] - delta
                        triangles.Add(newTriangle);
                        //meshIndexes.Add(extMesh);
                    }

                    //extMesh.Indexes = model.IndexBuffer.Select(item => item - (extMesh.StartVertice - 1)).ToArray();
                }
                /*
            else
            {
                for (int i = 0; i < GeoData.Vertices.Length; i += 3)
                {
                    var tri = new TriangleInfo(Array.IndexOf(GeoData.Vertices, GeoData.Vertices[i]),
                                               Array.IndexOf(GeoData.Vertices, GeoData.Vertices[i + 1]),
                                               Array.IndexOf(GeoData.Vertices, GeoData.Vertices[i + 2]));
                    triangles.Add(tri);
                    //meshIndexes.Add(extMesh);
                    index++;
                }
            }*/
                var endTriangle = triangles.Count;
                if (model.IndexData.Any())
                {
                    verticesCount += (model.IndexData.Max() - model.IndexData.Min());
                    mesh.InitMesh(model.IndexData.Min(), model.IndexData.Max(), startTriangle, endTriangle,
                        verticesCount);
                    mesh.Bounds = new AABB(model.VertexData.ToPointList());

                    for (int n = startTriangle; n < endTriangle; n++)
                        meshTriangleMap.Add(n, mesh);
                }
                else
                {
                    Tracer.TraceLine("Model doesnt have index data! " +model.Name);
                }
            }
            Triangles = triangles.ToArray();
        }

        private GeometryProfile CreateGeometryProfile(GeometryInfo geometryInfo)
        {
            if (geometryInfo.Name.Contains("lt"))
            {
                return new LightsourceProfile() { Name = geometryInfo.Name, MaterialName = geometryInfo.MaterialName };
            }

            if (geometryInfo.Name.Contains("volume"))
            {
                return new VolumeProfile() { Name = geometryInfo.Name, MaterialName = geometryInfo.MaterialName };
            }

            return new SurfaceProfile() { Name = geometryInfo.Name, MaterialName = geometryInfo.MaterialName };
        }
    }
}